package br.com.cefetrn.apoena.dominio.home;

import static org.hibernate.criterion.Example.create;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;

public abstract class AxHomeGeneric<G, ID extends Serializable> implements
		IxHomeGeneric<G, ID> {

	private Class<G> persistentClass;

	private Session session;

	@SuppressWarnings("unchecked")
	public AxHomeGeneric() {
		this.persistentClass = (Class<G>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}

	public void setSession(Session session) {
		IllegalStateException e = null;
		if (session == null) {
			e = new IllegalStateException(
					"Session has not been set on DAO before usage");
			log.error(e);
			throw e;
		} else
			this.session = session;
	}

	protected Session getSession() {
		IllegalStateException e = null;
		if (session == null) {
			e = new IllegalStateException(
					"Session has not been set on DAO before usage");
			log.error(e);
			throw e;
		}
		return session;
	}

	public Class<G> getPersistentClass() {
		return persistentClass;
	}

	@SuppressWarnings("unchecked")
	public G findById(ID id) {
		log.debug("getting " + getPersistentClass() + " instance with id: "
				+ id);
		try {
			G instance = (G) getSession().get(getPersistentClass(), id);
			if (instance == null) {
				log.debug("get successful, no instance found");
			} else {
				log.debug("get successful, instance found");
			}
			return instance;
		} catch (RuntimeException re) {
			log.error("get failed", re);
			throw re;
		}
	}

	@SuppressWarnings("unchecked")
	public List<G> findAll() {
		return findByCriteria();
	}

	@SuppressWarnings("unchecked")
	public G persist(G instance) {
		log.debug("persisting " + getPersistentClass() + " instance");
		try {
			getSession().beginTransaction();
			getSession().persist(instance);
			getSession().getTransaction().commit();			
			log.debug("persist successful");
			return instance;
		} catch (RuntimeException re) {
			log.error("persist failed", re);
			throw re;
		}
	}

	public G attachDirty(G instance) {
		log.debug("attaching dirty " + getPersistentClass() + " instance");
		try {
			getSession().beginTransaction();
			getSession().saveOrUpdate(instance);
			getSession().getTransaction().commit();
			log.debug("attach successful");
			return instance;
		} catch (RuntimeException re) {
			log.error("attach failed", re);
			throw re;
		}
	}

	public G attachClean(G instance) {
		log.debug("attaching clean " + getPersistentClass() + " instance");
		try {
			getSession().lock(instance, LockMode.NONE);
			log.debug("attach successful");
			return instance;
		} catch (RuntimeException re) {
			log.error("attach failed", re);
			throw re;
		}
	}

	@SuppressWarnings("unchecked")
	public G merge(G detachedInstance) {
		log.debug("merging " + getPersistentClass() + " instance");
		try {
			G result = (G) getSession().merge(detachedInstance);
			log.debug("merge successful");
			return result;
		} catch (RuntimeException re) {
			log.error("merge failed", re);
			throw re;
		}
	}

	public G delete(G persistentInstance) {
		log.debug("deleting " + getPersistentClass() + " instance");
		try {
			getSession().beginTransaction();
			getSession().delete(persistentInstance);
			getSession().getTransaction().commit();
			log.debug("delete successful");
			return persistentInstance;
		} catch (RuntimeException re) {
			log.error("delete failed", re);
			throw re;
		}
	}

	@SuppressWarnings("unchecked")
	public List<G> findByExample(G instance) {
		log.debug("finding " + getPersistentClass() + " instance by example");
		try {
			List<G> results = (List<G>) getSession().createCriteria(
					getPersistentClass()).add(create(instance)).list();
			log.debug("find by example successful, result size: "
					+ results.size());
			return results;
		} catch (RuntimeException re) {
			log.error("find by example failed", re);
			throw re;
		}
	}

	@SuppressWarnings("unchecked")
	protected List<G> findByCriteria(Criterion... criterion) {
		Criteria crit = getSession().createCriteria(getPersistentClass());
		for (Criterion c : criterion) {
			crit.add(c);
		}
		return crit.list();
	}

	@SuppressWarnings("unchecked")
	public List<G> findByExample(G exampleInstance, String[] excludeProperty) {
		Criteria crit = getSession().createCriteria(getPersistentClass());
		Example example = Example.create(exampleInstance);
		for (String exclude : excludeProperty) {
			example.excludeProperty(exclude);
		}
		crit.add(example);
		return crit.list();
	}

	public void flush() {
		getSession().flush();
	}

	public void clear() {
		getSession().clear();
	}
}
